﻿using arcsoft_usb_video.Entity;
using ArcSoftFace.Entity;
using ArcSoftFace.SDKModels;
using ArcSoftFace.SDKUtil;
using ArcSoftFace.Utils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace arcsoft_usb_video.SDKUtil
{
    class ArcSoftSDK
    {
        private int retCode;
        private IntPtr pImageEngine = IntPtr.Zero;
        private IntPtr pVideoEngine = IntPtr.Zero;
        private IntPtr pVideoRGBImageEngine = IntPtr.Zero;
        private IntPtr pVideoIRImageEngine = IntPtr.Zero;

        private readonly Dictionary<string, IntPtr> keyValuePairs = new Dictionary<string, IntPtr>();
        public void InitSDK()
        {
            //读取配置文件
            AppSettingsReader reader = new AppSettingsReader();
            string appid = (string)reader.GetValue("appid", typeof(string));
            string appkey = (string)reader.GetValue("appkey", typeof(string));
            try
            {
                this.retCode = ASFFunctions.ASFActivation(appid, appkey);
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("无法加载 DLL"))
                {
                    MessageBox.Show("请将sdk相关DLL放入bin对应的x86或x64下的文件夹中!");
                }
                else
                {
                    MessageBox.Show("激活引擎失败!");
                }
                return;
            }

            //初始化引擎
            uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE;
            //Video模式下检测脸部的角度优先值
            int videoDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_HIGHER_EXT;
            //Image模式下检测脸部的角度优先值
            int imageDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_ONLY;
            //人脸在图片中所占比例，如果需要调整检测人脸尺寸请修改此值，有效数值为2-32
            int detectFaceScaleVal = 16;
            //最大需要检测的人脸个数
            int detectFaceMaxNum = 5;
            //引擎初始化时需要初始化的检测功能组合
            int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE;
            //初始化引擎，正常值为0，其他返回值请参考http://ai.arcsoft.com.cn/bbs/forum.php?mod=viewthread&tid=19&_dsign=dbad527e
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pImageEngine);
            Console.WriteLine("InitEngine Result:" + retCode);

            //初始化视频模式下人脸检测引擎
            uint detectModeVideo = DetectionMode.ASF_DETECT_MODE_VIDEO;
            int combinedMaskVideo = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION;
            retCode = ASFFunctions.ASFInitEngine(detectModeVideo, videoDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMaskVideo, ref pVideoEngine);
            //RGB视频专用FR引擎
            detectFaceMaxNum = 1;
            combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_LIVENESS;
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoRGBImageEngine);

            //IR视频专用FR引擎
            combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_IR_LIVENESS;
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoIRImageEngine);

            Console.WriteLine("InitVideoEngine Result:" + retCode);
        }

        public Rectangle DetectFace(Bitmap bitmap)
        {
            //检测人脸，得到Rect框
            ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap);
            //得到最大人脸
            ASF_SingleFaceInfo maxFace = FaceUtil.GetMaxFace(multiFaceInfo);
            //得到Rect
            MRECT rect = maxFace.faceRect;
            return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
        }

        public FaceInfo SearchFace(Image bitmap)
        {
            ASF_SingleFaceInfo singleFaceInfo;
            IntPtr feature = FaceUtil.ExtractFeature(pVideoRGBImageEngine, bitmap, out singleFaceInfo);
            if (singleFaceInfo.faceRect.left == 0 && singleFaceInfo.faceRect.right == 0)
            {
                Console.WriteLine("未检测到人脸");
                return new FaceInfo();
            }

            foreach (var kvp in keyValuePairs)
            {
                float similarity = 0f;
                ASFFunctions.ASFFaceFeatureCompare(pImageEngine,feature,kvp.Value,ref similarity);
                if(similarity > 0.8)
                {
                    Console.WriteLine("匹配到人脸{0}", kvp.Key);

                    MRECT faceRect = singleFaceInfo.faceRect;
                    FaceInfo info = new FaceInfo
                    {
                        key = kvp.Key,
                        rectangle = new Rectangle(faceRect.left, faceRect.top, faceRect.right - faceRect.left, faceRect.bottom - faceRect.top)

                    };
                    return info;
                }
            }

            Console.WriteLine("未匹配到人脸");
            return new FaceInfo();
        }

        public bool AddFace(String key , byte[] data)
        {
            keyValuePairs.Remove(key);
            ASF_FaceFeature faceFeature = new ASF_FaceFeature
            {
                featureSize = data.Length,
                feature = MemoryUtil.Malloc(data.Length)
            };
            MemoryUtil.Copy(data, 0, faceFeature.feature, faceFeature.featureSize);

            IntPtr pFaceFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>());
            MemoryUtil.StructureToPtr(faceFeature, pFaceFeature);
            
            keyValuePairs.Add(key, pFaceFeature);
            Console.WriteLine("添加用户到对比库：{0}", key);
            return true;
        }

        public byte[] FaceFeatureData(Bitmap bitmap,Rectangle rectangle)
        {
            ASF_SingleFaceInfo singleFaceInfo = new ASF_SingleFaceInfo() {
                faceOrient = 0,
                faceRect = new MRECT() { left = rectangle.Left,right = rectangle.Right,top = rectangle.Top,bottom = rectangle.Bottom}
            };
            IntPtr pFaceFeature = FaceUtil.ExtractFeature(pImageEngine, bitmap, out singleFaceInfo);
            ASF_FaceFeature faceFeature = MemoryUtil.PtrToStructure<ASF_FaceFeature>(pFaceFeature);

            byte[] feature = new byte[faceFeature.featureSize];
            MemoryUtil.Copy(faceFeature.feature, feature, 0, faceFeature.featureSize);

            MemoryUtil.Free(pFaceFeature);

            return feature;
        }

        public ASF_Face3DAngle Face3DAngle(Bitmap bitmap)
        {
            int retCode;
            ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap);
            ImageInfo imageInfo = ImageUtil.ReadBMP(bitmap);
            ASF_Face3DAngle face3DAngle = FaceUtil.Face3DAngleDetection(pImageEngine, imageInfo, multiFaceInfo, out retCode);
            return face3DAngle;
        }

        public bool Live(Image bitmap)
        {
            int retCode;
            bitmap = ImageUtil.ScaleImage(bitmap, bitmap.Width, bitmap.Height);
            ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap);
            ImageInfo imageInfo = ImageUtil.ReadBMP(bitmap);
            ASF_LivenessInfo aSF_LivenessInfo = FaceUtil.LivenessInfo_RGB(pVideoRGBImageEngine, imageInfo, multiFaceInfo, out retCode);
            if (aSF_LivenessInfo.num == 0)
            {
                MemoryUtil.Free(imageInfo.imgData);
                MemoryUtil.Free(aSF_LivenessInfo.isLive);
                return false;
            }

            Int32 isLive = MemoryUtil.PtrToStructure<Int32>(aSF_LivenessInfo.isLive);
            bool b = isLive != 0;
            MemoryUtil.Free(imageInfo.imgData);
            return b;
        }
    }
}
